{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ">作者 : *Didrik Pinte*\n",
    "\n",
    "Traits项目允许你可以向Python项目属性方便的添加验证、初始化、委托、通知和图形化界面。\n",
    "\n",
    "在这个教程中，我们将研究Traits工具包并且学习如何动态减少你所写的锅炉片代码，进行快速的GUI应用开发，以及理解Enthought工具箱中其他部分的想法。\n",
    "\n",
    "Traits和Enthought工具箱是基于BSD-style证书的开源项目。\n",
    "\n",
    "---\n",
    "**目标受众**\n",
    "\n",
    "Python中高级程序员\n",
    "---\n",
    "\n",
    "---\n",
    "**要求**\n",
    "\n",
    "- [wxPython](http://www.wxpython.org/)、[PyQt](https://riverbankcomputing.com/software/pyqt/intro)或[PySide](https://pyside.github.io/docs/pyside/)之一\n",
    "- Numpy和Scipy\n",
    "- [Enthought工具箱](http://code.enthought.com/projects)\n",
    "- 所有需要的软件都可以通过安装[EPD免费版](https://store.enthought.com/)来获得\n",
    "---\n",
    "\n",
    "---\n",
    "\n",
    "**教程内容**\n",
    "- 介绍\n",
    "- 例子\n",
    "- Traits是什么\n",
    "    - 初始化\n",
    "    - 验证\n",
    "    - 文档\n",
    "    - 可视化: 打开一个对话框\n",
    "    - 推迟\n",
    "    - 通知\n",
    "    - 一些更高级的特征\n",
    "    \n",
    "## 3.4.1 介绍\n",
    "\n",
    "Enthought工具箱可以构建用于数据分析、2D绘图和3D可视化的精密应用框架。这些强力可重用的组块是在BSD-style证书下发布的。\n",
    "\n",
    "Enthought工具箱主要的包是：\n",
    "\n",
    "- Traits - 基于组块的方式构建我们的应用。\n",
    "- Kiva - 2D原生支持基于路径的rendering、affine转化、alpha混合及其它。\n",
    "- Enable - 基于对象的2D绘图画布。\n",
    "- Chaco - 绘图工具箱，用于构建复杂的交互2D图像。\n",
    "- Mayavi -基于VTK的3D科学数据可视化\n",
    "- Envisage - 应用插件框架，用于构建脚本化可扩展的应用\n",
    "\n",
    "![](http://www.scipy-lectures.org/_images/ETS.jpg)\n",
    "\n",
    "在这篇教程中，我们将关注Traits。\n",
    "\n",
    "## 3.4.2 例子\n",
    "\n",
    "在整个这篇教程中，我们将使用基于水资源管理简单案例的一个样例。我们将试着建模一个水坝和水库系统。水库和水坝有下列参数：\n",
    "\n",
    "- 名称\n",
    "- 水库的最小和最大容量 [$hm^3$]\n",
    "- 水坝的高度和宽度[$m$]\n",
    "- 蓄水面积[$km^2$]\n",
    "- 水压头[$m$]\n",
    "- 涡轮的动力[$MW$]\n",
    "- 最小和最大放水量[$m^3/s$]\n",
    "- 涡轮的效率\n",
    "\n",
    "水库有一个已知的运转情况。一部分是与基于放水量有关的能量产生。估算水力发电机电力生产的简单公式是$P = \\rho hrgk$, 其中\n",
    "\n",
    "- P 以瓦特为单位的功率, \n",
    "- \\rho 是水的密度 ($~1000 kg/m^3$),\n",
    "- h 是水的高度,\n",
    "- r 是以每秒立方米为单位的流动率,\n",
    "- g 重力加速度，9.8 $m/s^2$,\n",
    "- k 是效率系数，范围从0到1。\n",
    "\n",
    "年度的电能生产取决于可用的水供给。在一些设施中，水流率在一年中可能差10倍。\n",
    "\n",
    "运行状态的第二个部分是蓄水量，蓄水量(storage)依赖于控制和非控制参数：\n",
    "\n",
    "$storage_{t+1} = storage_t + inflows - release - spillage - irrigation$\n",
    "\n",
    "---\n",
    "本教程中使用的数据不是真实的，可能甚至在现实中没有意义。\n",
    "\n",
    "---\n",
    "\n",
    "## 3.4.3 Traits是什么\n",
    "\n",
    "trait是可以用于常规Python对象属性的类型定义，给出属性的一些额外特性:\n",
    "\n",
    "- 标准化:\n",
    "    - 初始化\n",
    "    - 验证\n",
    "    - 推迟\n",
    "- 通知\n",
    "- 可视化\n",
    "- 文档\n",
    "\n",
    "类可以自由混合基于trait的属性与通用Python属性，或者选择允许在这个类中只使用固定的或开放的trait属性集。类定义的Trait属性自动继承自由这个类衍生的其他子类。\n",
    "\n",
    "创建一个traits类的常用方式是通过扩展**HasTraits**基础类，并且定义类的traits :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from traits.api import HasTraits, Str, Float\n",
    "\n",
    "class Reservoir(HasTraits):\n",
    "\n",
    "    name = Str\n",
    "    max_storage = Float"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "对Traits 3.x用户来说\n",
    "\n",
    "如果使用Traits 3.x, 你需要调整traits包的命名空间:\n",
    "\n",
    "- traits.api应该为enthought.traits.api\n",
    "- traitsui.api应该为enthought.traits.ui.api\n",
    "\n",
    "---\n",
    "\n",
    "像这样使用traits类和使用其他Python类一样简单。注意，trait值通过关键词参数传递:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "reservoir = Reservoir(name='Lac de Vouglans', max_storage=605)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.4.3.1 初始化\n",
    "\n",
    "所有的traits都有一个默认值来初始化变量。例如，基础python类型有如下的trait等价物:\n",
    "\n",
    "\n",
    "|Trait|Python类型|内置默认值|\n",
    "|-----|-----------|----------------------|\n",
    "|Bool|Boolean|False|\n",
    "|Complex|Complex number|0+0j|\n",
    "|Float|Floating point number|0.0|\n",
    "|Int|Plain integer|0|\n",
    "|Long|Long integer|0L|\n",
    "|Str|String|''|\n",
    "|Unicode|Unicode|u''|\n",
    "\n",
    "存在很多其他预定义的trait类型: Array, Enum, Range, Event, Dict, List, Color, Set, Expression, Code, Callable, Type, Tuple, etc。\n",
    "\n",
    "自定义默认值可以在代码中定义:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from traits.api import HasTraits, Str, Float\n",
    "\n",
    "class Reservoir(HasTraits):\n",
    "\n",
    "    name = Str\n",
    "    max_storage = Float(100)\n",
    "\n",
    "reservoir = Reservoir(name='Lac de Vouglans')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "复杂初始化\n",
    "\n",
    "当一个trait需要复杂的初始化时，可以实施\\_XXX\\_默认魔法方法。当调用XXX trait时，它会被懒惰的调用。例如：\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def _name_default(self):\n",
    "    \"\"\" Complex initialisation of the reservoir name. \"\"\"\n",
    "\n",
    "    return 'Undefined'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.4.3.2 验证\n",
    "\n",
    "当用户试图设置trait的内容时，每一个trait都会被验证:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "ename": "TraitError",
     "evalue": "The 'max_storage' trait of a Reservoir instance must be a float, but a value of '230' <type 'str'> was specified.",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTraitError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-5-cbed071af0b9>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0mreservoir\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mReservoir\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'Lac de Vouglans'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmax_storage\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m605\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mreservoir\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmax_storage\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'230'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m/Library/Python/2.7/site-packages/traits/trait_handlers.pyc\u001b[0m in \u001b[0;36merror\u001b[0;34m(self, object, name, value)\u001b[0m\n\u001b[1;32m    170\u001b[0m         \"\"\"\n\u001b[1;32m    171\u001b[0m         raise TraitError( object, name, self.full_info( object, name, value ),\n\u001b[0;32m--> 172\u001b[0;31m                           value )\n\u001b[0m\u001b[1;32m    173\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    174\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mfull_info\u001b[0m \u001b[0;34m(\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mobject\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mTraitError\u001b[0m: The 'max_storage' trait of a Reservoir instance must be a float, but a value of '230' <type 'str'> was specified."
     ]
    }
   ],
   "source": [
    "reservoir = Reservoir(name='Lac de Vouglans', max_storage=605)\n",
    "\n",
    "reservoir.max_storage = '230'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.4.3.3 文档\n",
    "\n",
    "从本质上说，所有的traits都提供关于模型自身的文档。创建类的声明方式使它是自解释的:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from traits.api import HasTraits, Str, Float\n",
    "\n",
    "class Reservoir(HasTraits):\n",
    "\n",
    "    name = Str\n",
    "    max_storage = Float(100)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "trait的**desc**元数据可以用来提供关于trait更多的描述信息:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from traits.api import HasTraits, Str, Float\n",
    "\n",
    "class Reservoir(HasTraits):\n",
    "\n",
    "    name = Str\n",
    "    max_storage = Float(100, desc='Maximal storage [hm3]')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在让我们来定义完整的reservoir类:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Releasing 80 m3/s produces 1.3561344e+11 kWh\n"
     ]
    }
   ],
   "source": [
    "from traits.api import HasTraits, Str, Float, Range\n",
    "\n",
    "class Reservoir(HasTraits):\n",
    "    name = Str\n",
    "    max_storage = Float(1e6, desc='Maximal storage [hm3]')\n",
    "    max_release = Float(10, desc='Maximal release [m3/s]')\n",
    "    head = Float(10, desc='Hydraulic head [m]')\n",
    "    efficiency = Range(0, 1.)\n",
    "\n",
    "    def energy_production(self, release):\n",
    "        ''' Returns the energy production [Wh] for the given release [m3/s]\n",
    "        '''\n",
    "        power = 1000 * 9.81 * self.head * release * self.efficiency\n",
    "        return power * 3600\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    reservoir = Reservoir(\n",
    "                        name = 'Project A',\n",
    "                        max_storage = 30,\n",
    "                        max_release = 100.0,\n",
    "                        head = 60,\n",
    "                        efficiency = 0.8\n",
    "                    )\n",
    "\n",
    "    release = 80\n",
    "    print 'Releasing {} m3/s produces {} kWh'.format(\n",
    "                        release, reservoir.energy_production(release)\n",
    "                    )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.4.3.4 可视化: 打开一个对话框\n",
    "\n",
    "Traits库也关注用户界面，可以弹出一个Reservoir类的默认视图:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "reservoir1 = Reservoir()\n",
    "reservoir1.edit_traits()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](http://www.scipy-lectures.org/_images/reservoir_default_view.png)\n",
    "\n",
    "TraitsUI简化了创建用户界面的方式。HasTraits类上的每一个trait都有一个默认的编辑器，将管理trait在屏幕上显示的方式 (即Range trait显示为一个滑块等)。\n",
    "\n",
    "与Traits声明方式来创建类的相同渠道，TraitsUI提供了声明的界面来构建用户界面代码:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from traits.api import HasTraits, Str, Float, Range\n",
    "from traitsui.api import View\n",
    "\n",
    "class Reservoir(HasTraits):\n",
    "    name = Str\n",
    "    max_storage = Float(1e6, desc='Maximal storage [hm3]')\n",
    "    max_release = Float(10, desc='Maximal release [m3/s]')\n",
    "    head = Float(10, desc='Hydraulic head [m]')\n",
    "    efficiency = Range(0, 1.)\n",
    "\n",
    "    traits_view = View(\n",
    "        'name', 'max_storage', 'max_release', 'head', 'efficiency',\n",
    "        title = 'Reservoir',\n",
    "        resizable = True,\n",
    "    )\n",
    "\n",
    "    def energy_production(self, release):\n",
    "        ''' Returns the energy production [Wh] for the given release [m3/s]\n",
    "        '''\n",
    "        power = 1000 * 9.81 * self.head * release * self.efficiency \n",
    "        return power * 3600\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    reservoir = Reservoir(\n",
    "                        name = 'Project A',\n",
    "                        max_storage = 30,\n",
    "                        max_release = 100.0,\n",
    "                        head = 60,\n",
    "                        efficiency = 0.8\n",
    "                    )\n",
    "\n",
    "    reservoir.configure_traits()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](http://www.scipy-lectures.org/_images/reservoir_view.png)\n",
    "\n",
    "### 3.4.3.5 推迟\n",
    "\n",
    "可以将trait定义和它的值推送给另一个对象是Traits的有用的功能。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from traits.api import HasTraits, Instance, DelegatesTo, Float, Range\n",
    "\n",
    "from reservoir import Reservoir\n",
    "\n",
    "class ReservoirState(HasTraits):\n",
    "    \"\"\"Keeps track of the reservoir state given the initial storage.\n",
    "    \"\"\"\n",
    "    reservoir = Instance(Reservoir, ())\n",
    "    min_storage = Float\n",
    "    max_storage = DelegatesTo('reservoir')\n",
    "    min_release = Float\n",
    "    max_release = DelegatesTo('reservoir')\n",
    "\n",
    "    # state attributes\n",
    "    storage = Range(low='min_storage', high='max_storage')\n",
    "\n",
    "    # control attributes\n",
    "    inflows =  Float(desc='Inflows [hm3]')\n",
    "    release = Range(low='min_release', high='max_release')\n",
    "    spillage = Float(desc='Spillage [hm3]')\n",
    "\n",
    "    def print_state(self):\n",
    "        print 'Storage\\tRelease\\tInflows\\tSpillage'\n",
    "        str_format = '\\t'.join(['{:7.2f}'for i in range(4)])\n",
    "        print str_format.format(self.storage, self.release, self.inflows,\n",
    "                self.spillage)\n",
    "        print '-' * 79\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    projectA = Reservoir(\n",
    "            name = 'Project A',\n",
    "            max_storage = 30,\n",
    "            max_release = 100.0,\n",
    "            hydraulic_head = 60,\n",
    "            efficiency = 0.8\n",
    "        )\n",
    "\n",
    "    state = ReservoirState(reservoir=projectA, storage=10)\n",
    "    state.release = 90\n",
    "    state.inflows = 0\n",
    "    state.print_state()\n",
    "\n",
    "    print 'How do we update the current storage ?'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "特殊的trait允许用魔法\\_xxxx\\_fired方法管理事件和触发器函数:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from traits.api import HasTraits, Instance, DelegatesTo, Float, Range, Event\n",
    "\n",
    "from reservoir import Reservoir\n",
    "\n",
    "class ReservoirState(HasTraits):\n",
    "    \"\"\"Keeps track of the reservoir state given the initial storage.\n",
    "\n",
    "    For the simplicity of the example, the release is considered in\n",
    "    hm3/timestep and not in m3/s.\n",
    "    \"\"\"\n",
    "    reservoir = Instance(Reservoir, ())\n",
    "    min_storage = Float\n",
    "    max_storage = DelegatesTo('reservoir')\n",
    "    min_release = Float\n",
    "    max_release = DelegatesTo('reservoir')\n",
    "\n",
    "    # state attributes\n",
    "    storage = Range(low='min_storage', high='max_storage')\n",
    "\n",
    "    # control attributes\n",
    "    inflows =  Float(desc='Inflows [hm3]')\n",
    "    release = Range(low='min_release', high='max_release')\n",
    "    spillage = Float(desc='Spillage [hm3]')\n",
    "\n",
    "    update_storage = Event(desc='Updates the storage to the next time step')\n",
    "\n",
    "    def _update_storage_fired(self):\n",
    "        # update storage state\n",
    "        new_storage = self.storage - self.release  + self.inflows\n",
    "        self.storage = min(new_storage, self.max_storage)\n",
    "        overflow = new_storage - self.max_storage\n",
    "        self.spillage = max(overflow, 0)\n",
    "\n",
    "    def print_state(self):\n",
    "        print 'Storage\\tRelease\\tInflows\\tSpillage'\n",
    "        str_format = '\\t'.join(['{:7.2f}'for i in range(4)])\n",
    "        print str_format.format(self.storage, self.release, self.inflows,\n",
    "                self.spillage)\n",
    "        print '-' * 79\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    projectA = Reservoir(\n",
    "        name = 'Project A',\n",
    "        max_storage = 30,\n",
    "        max_release = 5.0,\n",
    "        hydraulic_head = 60,\n",
    "        efficiency = 0.8\n",
    "    )\n",
    "\n",
    "    state = ReservoirState(reservoir=projectA, storage=15)\n",
    "    state.release = 5\n",
    "    state.inflows = 0\n",
    "\n",
    "    # release the maximum amount of water during 3 time steps\n",
    "    state.update_storage = True\n",
    "    state.print_state()\n",
    "    state.update_storage = True\n",
    "    state.print_state()\n",
    "    state.update_storage = True\n",
    "    state.print_state()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对象间的依赖可以自动使用trait**Property**完成。**depends_on**属性表示property其他traits的依赖性。当其他traits改变了,property是无效的。此外，Traits为属性使用魔法函数的名字:\n",
    "- \\_get\\_XXX 来获得XXX属性的trait\n",
    "- \\_set\\_XXX 来设置XXX属性的trait"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from traits.api import HasTraits, Instance, DelegatesTo, Float, Range\n",
    "from traits.api import Property\n",
    "\n",
    "from reservoir import Reservoir\n",
    "\n",
    "class ReservoirState(HasTraits):\n",
    "    \"\"\"Keeps track of the reservoir state given the initial storage.\n",
    "\n",
    "    For the simplicity of the example, the release is considered in\n",
    "    hm3/timestep and not in m3/s.\n",
    "    \"\"\"\n",
    "    reservoir = Instance(Reservoir, ())\n",
    "    max_storage = DelegatesTo('reservoir')\n",
    "    min_release = Float\n",
    "    max_release = DelegatesTo('reservoir')\n",
    "\n",
    "    # state attributes\n",
    "    storage = Property(depends_on='inflows, release')\n",
    "\n",
    "    # control attributes\n",
    "    inflows =  Float(desc='Inflows [hm3]')\n",
    "    release = Range(low='min_release', high='max_release')\n",
    "    spillage = Property(\n",
    "            desc='Spillage [hm3]', depends_on=['storage', 'inflows', 'release']\n",
    "        )\n",
    "\n",
    "    ### Private traits.\n",
    "    _storage = Float\n",
    "\n",
    "    ### Traits property implementation.\n",
    "    def _get_storage(self):\n",
    "        new_storage = self._storage - self.release + self.inflows\n",
    "        return min(new_storage, self.max_storage)\n",
    "\n",
    "    def _set_storage(self, storage_value):\n",
    "        self._storage = storage_value\n",
    "\n",
    "    def _get_spillage(self):\n",
    "        new_storage = self._storage - self.release  + self.inflows\n",
    "        overflow = new_storage - self.max_storage\n",
    "        return max(overflow, 0)\n",
    "\n",
    "    def print_state(self):\n",
    "        print 'Storage\\tRelease\\tInflows\\tSpillage'\n",
    "        str_format = '\\t'.join(['{:7.2f}'for i in range(4)])\n",
    "        print str_format.format(self.storage, self.release, self.inflows,\n",
    "                self.spillage)\n",
    "        print '-' * 79\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    projectA = Reservoir(\n",
    "                    name = 'Project A',\n",
    "                    max_storage = 30,\n",
    "                    max_release = 5,\n",
    "                    hydraulic_head = 60,\n",
    "                    efficiency = 0.8\n",
    "                )\n",
    "\n",
    "    state = ReservoirState(reservoir=projectA, storage=25)\n",
    "    state.release = 4\n",
    "    state.inflows = 0\n",
    "\n",
    "    state.print_state()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "**注意** 缓存属性\n",
    "当访问一个输入没有改变的属性时，大量计算或长时间运行的计算会是个问题。@cached_property修饰器可以用来缓存这个值，并且只有在失效时才会重新计算一次他们。\n",
    "\n",
    "---\n",
    "让我们用ReservoirState的例子来扩展TraitsUI介绍:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from traits.api import HasTraits, Instance, DelegatesTo, Float, Range, Property\n",
    "from traitsui.api import View, Item, Group, VGroup\n",
    "\n",
    "from reservoir import Reservoir\n",
    "\n",
    "class ReservoirState(HasTraits):\n",
    "    \"\"\"Keeps track of the reservoir state given the initial storage.\n",
    "\n",
    "    For the simplicity of the example, the release is considered in\n",
    "    hm3/timestep and not in m3/s.\n",
    "    \"\"\"\n",
    "    reservoir = Instance(Reservoir, ())\n",
    "    name = DelegatesTo('reservoir')\n",
    "    max_storage = DelegatesTo('reservoir')\n",
    "    max_release = DelegatesTo('reservoir')\n",
    "    min_release = Float\n",
    "\n",
    "    # state attributes\n",
    "    storage = Property(depends_on='inflows, release')\n",
    "\n",
    "    # control attributes\n",
    "    inflows =  Float(desc='Inflows [hm3]')\n",
    "    release = Range(low='min_release', high='max_release')\n",
    "    spillage = Property(\n",
    "            desc='Spillage [hm3]', depends_on=['storage', 'inflows', 'release']\n",
    "        )\n",
    "\n",
    "    ### Traits view\n",
    "    traits_view = View(\n",
    "        Group(\n",
    "            VGroup(Item('name'), Item('storage'), Item('spillage'),\n",
    "                label = 'State', style = 'readonly'\n",
    "            ),\n",
    "            VGroup(Item('inflows'), Item('release'), label='Control'),\n",
    "        )\n",
    "    )\n",
    "\n",
    "    ### Private traits.\n",
    "    _storage = Float\n",
    "\n",
    "    ### Traits property implementation.\n",
    "    def _get_storage(self):\n",
    "        new_storage = self._storage - self.release + self.inflows\n",
    "        return min(new_storage, self.max_storage)\n",
    "\n",
    "    def _set_storage(self, storage_value):\n",
    "        self._storage = storage_value\n",
    "\n",
    "    def _get_spillage(self):\n",
    "        new_storage = self._storage - self.release  + self.inflows\n",
    "        overflow = new_storage - self.max_storage\n",
    "        return max(overflow, 0)\n",
    "\n",
    "    def print_state(self):\n",
    "        print 'Storage\\tRelease\\tInflows\\tSpillage'\n",
    "        str_format = '\\t'.join(['{:7.2f}'for i in range(4)])\n",
    "        print str_format.format(self.storage, self.release, self.inflows,\n",
    "                self.spillage)\n",
    "        print '-' * 79\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    projectA = Reservoir(\n",
    "        name = 'Project A',\n",
    "        max_storage = 30,\n",
    "        max_release = 5,\n",
    "        hydraulic_head = 60,\n",
    "        efficiency = 0.8\n",
    "    )\n",
    "\n",
    "    state = ReservoirState(reservoir=projectA, storage=25)\n",
    "    state.release = 4\n",
    "    state.inflows = 0\n",
    "\n",
    "    state.print_state()\n",
    "    state.configure_traits()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](http://www.scipy-lectures.org/_images/reservoir_state_view.png)\n",
    "\n",
    "Some use cases need the delegation mechanism to be broken by the user when setting the value of the trait. The PrototypeFrom trait implements this behaviour."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "TraitsUI simplifies the way user interfaces are created. Every trait on a HasTraits class has a default editor that will manage the way the trait is rendered to the screen (e.g. the Range trait is displayed as a slider, etc.).\n",
    "In the very same vein as the Traits declarative way of creating classes, TraitsUI provides a declarative interface to build user interfaces code:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
